在 Nutanix Beam(Saas 产品)中,我们通过处理大量数据来了解云服务支出和云安全性。Nutanix Beam 建立在微服务和服务网格架构上,通过 Consul、Nomad、Vault、Envoy、Docker 等进行同步 RPC 样式请求。本文将不对微服务体系架构进行更多讨论,本文的重点在于支持该架构的关键技术——团队和微服务之间的异步通信。
我们使用 Disque & Conductor 进行批处理。这两个系统都是基于队列的系统。如果想要在平台中添加发布/订阅功能,那么就需要找到一个能够可靠存储事件并在需要时进行回放的流媒体平台。因为我们团队中有些人曾使用过 Kafka,并且在中等规模的 Kafka 集群有水机操作的经验,所以我们有理由选择它。在部署一项技术之前,我们需要考量以下两个方面:考虑当前的环境,并验证技术选择,这样我们才不会选择很熟悉但可能不适合项目的技术。另外,需要谨慎地做出决定,因为流平台将成为这个 SaaS 产品的重要组成部分。
我们对 Apache Pulsar 的最初了解源自于 GitHub 上一个 issue,了解后便决定使用我们自己的流平台用例进行评估。🙋♂️https://github.com/Yolean/kubernetes-kafka/issues/100#issuecomment-353968576Pulsar 是 Apache 的顶级开源项目,让我们确信这是一项成熟的技术。Apache Pulsar 已在雅虎线上运行多年,并被许多公司使用。我们收集了需要流平台的用例列表,并开始深入分析 Apache Pulsar 的架构、协调性、持久性、可靠性、高可用性、容错能力和客户端生态系统。
Apache Pulsar 是一个开源的分布式发布-订阅消息系统,创建于雅虎,现在是 Apache 软件基金会的一部分。
Apache Pulsar 的架构能解耦消息处理、服务和存储。存储层是 Apache BookKeeper,能够实现数据的持久性、多副本和高可用性。服务层由一组 broker 组成,并且由这一组 broker 处理消息服务,Pulsar Function 负责处理所在层消息。架构中的每一层都可以独立伸缩——独立伸缩 BookKeeper(存储层)可以调节存储量和吞吐量,伸缩 Pulsar broker(服务层)可以调节消息服务吞吐量,伸缩 function workers(处理层)可以处理更多数据。
更多关于 Apache Pulsar 的信息,可参考:
https://pulsar.apache.org/docs/en/concepts-overview/我们在收集用例时发现,我们都期待能够同时使用消息队列和发布-订阅模型来使用消息。但是,由于 worker 的最大数量受到分区数量的限制,并且不能对单条消息进行确认,我们不能将 Kafka 用作一个队列系统。在 Kafka 中,我们需要自己维护一个单条消息的确认记录来手动提交偏移量,但这增加了很多额外工作。
使用 Pulsar,你不必考虑使用哪个模型来 consume 数据。在 Pulsar 中,你可以 consume 或提交偏移量,也可以 consume 或确认单条消息。另外,如果 consumer 出现故障且定义了订阅模式的延迟配置,那么 Pulsar 会再次向另一 consumer 发布消息。
由于我们已经运营过中等规模的 Kafka 集群,对它的局限性也有了进一步的了解。我们看到 Pulsar 可以很好地应对这些问题,同时也能更好地满足我们的需求。在 Apache Kafka 中,用户滞后会导致随机读取,因此 producer 的吞吐量就会急剧下降。Kafka 的架构是这样设计的:高吞吐量依赖于顺序访问数据(以 append-only 方式)。
Apache Pulsar 通过结合分片架构和读写之间的隔离解决了这一问题,具体可参考下方链接。简而言之,热读由 broker 的内存缓存处理,而热写则由日志处理。事件回放/滞后消费从与日志位于不同磁盘的 ledger 读取访问数据。🙋♂️https://streaml.io/blog/access-patterns-and-tiered-storage-in-apache-pulsar
例如,假设在 1 个 topic 中有 5 个 consumer,他们都能正常读取消息(没有延迟)。在这种情况下,由 broker 缓存进行读取。后台进程定期将数据从 BookKeeper 日志转移到 ledger。
想象一下,我们的机器学习工程师需要从头开始 consume 这个 topic 的数据,以训练/再训练他的模型,因为他调整了模型的标准差和期望。因为滞后消费/事件回放是由位于不同磁盘上的 ledger 提供的,所以事件回放不会影响 topic 中用户最新数据的读取。
Apache Pulsar 通过其存储分层特性解决了这个问题,它可以将旧的数据卸载到可伸缩的存储系统中,如 Amazon s3、谷歌云存储、Minio 等。我们可以根据时间和存储空间大小配置卸载策略。一旦 topic 中的数据达到指定的时间或大小阈值,旧的数据段将会被移动至对象存储中。最重要的是,当使用者请求被卸载到对象存储中时,Pulsar 会透明地将这些数据从对象存储中提供给使用者。使用者不知道数据是来自磁盘还是来自对象存储。这样,topic 分区的大小就不受单个 broker 存储大小的限制。
向 Apache Kafka 集群添加节点不一定能解决与过载节点相关的性能问题,因为添加到 Kafka 集群的新节点将用于添加后创建的新 topic,但不会自动释放某些过载节点上的负载。如需使用它们来分散负载,就需要手动地重新平衡数据,将一些 topic 从旧节点迁移到新节点,而人工操作的成本极其昂贵。
在 Pulsar 中,由 Apache BookKeeper 提供存储层的存储。Pulsar 使用分片架构,其中 topic 分区中的消息被收集到分片中,然后被持久化。因此,topic 分片和节点之间不存在 Kafka 中的一对一映射。当添加新存储节点时,一些新段存储在新节点上,从而立即减少了先前存在节点上的负载。
我们用例的另一个重要需求是能够支持数百万个 topic 。例如,假设我们想要为一个用户回放数据。只要我们为每个用户创建一个 topic,就可以轻松地为特定用户的 topic 创建订阅,然后 consume 该用户的数据。删除用户数据就会变得像删除 topic 一样简单。
然而,在 Apache Kafka 中,扩展多个 topic 实际上是一个架构问题。Kafka 为每个 topic 创建多个文件(资源),因此,我们可以创建的 topic 数量将会受到限制。但是,如果想要把来自许多甚至所有用户的数据放在同一个 topic 中,那么在想回放单个用户数据时,则不得不回放整个 topic,并丢弃除所需用户消息以外的所有其他消息。即使使用客户 id 作为分区键的分区 topic,以便仅选择存储单个用户数据的分区,在 broker 和用户端仍然会浪费大量资源。
Pulsar 不会为每个 topic 创建文件。因为它使用 Apache BookKeeper 的分片存储架构,所以创建的分片数量不是由 topic 数量决定的。我们非常喜欢这一特性,因为它支持为每个用户创建一个 topic,这样的设计更简单高效。Pulsar已经在生产环境中运行了6个月,支持发布/订阅、队列和 schema registry。
总的来说,我们认为 Pulsar 极大地简化了 Nutanix Beam 中的异步通信设计。其可伸缩性和设计允许我们根据业务用例来决定如何 consume 数据,我们无需在获取数据时就做出决定。这种灵活性可以帮助我们更好地支持不断变化的业务需求。
想了解更多关于 Apache Pulsar 的信息,可以参考:
https://streamnative.io/blog/
Apache Pulsar 的社区也非常棒,社区的伙伴都乐于助人,对我们的疑问也及时进行解答与反馈。
每种技术都有属于它的位置。为了突出一些架构之间的差异,我们将 Pulsar 与 Kafka 进行了比较。我们过去曾使用过 kafka,也非常感谢 kafka 和 kafka 社区。
想要随时掌握 Pulsar 的研发进展、用户案例和热点话题吗?快来关注 Apache Pulsar 和 StreamNative 微信公众号,我们第一时间在这里分享与 Pulsar 有关的一切。
点击「阅读原文」查看英文原稿